﻿namespace System.IOC
{
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    public class IOContainer
    {

        // 字典，用于存放接口和类的实例
        private static ConcurrentDictionary<Type, ClassConfigInfo> InterfaceClassBinding = new ConcurrentDictionary<Type, ClassConfigInfo>();

        /// <summary>
        /// 私有化构造函数,单例模式
        /// </summary>
        private IOContainer() { }

        /// <summary>
        /// 单例类
        /// </summary>
        class IOContainerSingleton
        {
            public static IOContainer Instance = new IOContainer();
        }

        /// <summary>
        /// 获取IOContainer实例
        /// </summary>
        /// <returns></returns>
        public static IOContainer GetInstance()
        {
            return IOContainerSingleton.Instance;
        }

        /// <summary>
        /// 绑定接口与类 - 泛型
        /// </summary>
        /// <typeparam name="IApplicationService">接口</typeparam>
        /// <typeparam name="ApplicationService">类</typeparam>
        public IOContainer Bind<IApplicationService, ApplicationService>()
        {
            Type iService = typeof(IApplicationService);
            Type service = typeof(ApplicationService);
            return Bind(iService, service);
        }

        /// <summary>
        /// 绑定接口与类 - 类型
        /// </summary>
        /// <param name="iService"></param>
        /// <param name="service"></param>
        /// <returns></returns>
        public IOContainer Bind(Type iService, Type service)
        {
            // 如果该类型已经注册，那么抛出异常
            if (InterfaceClassBinding.ContainsKey(iService))
            {
                throw new Exception(string.Format("Type {0} already registered.", iService.FullName));
            }
            // 进行绑定
            InterfaceClassBinding.TryAdd(iService, new ClassConfigInfo(service) { });

            return this;
        }

        /// <summary>
        /// 绑定接口与类 - 程序集字符串
        /// </summary>
        /// <param name="iApplicationServiceStr">接口字符串 - "程序集名称,类型全称"</param>
        /// <param name="applicationServiceStr">实现类字符串 - "程序集名称,类型全称"</param>
        /// <returns></returns>
        public IOContainer Bind(string iApplicationServiceStr, string applicationServiceStr)
        {

            // 查找接口类型
            Type iApplicationServiceType = GetTypeByString(iApplicationServiceStr);

            // 查找实现类类型
            Type applicationServiceType = GetTypeByString(applicationServiceStr);

            // 绑定并返回
            return Bind(iApplicationServiceType, applicationServiceType);
        }

        /// <summary>
        ///  设置类型创建方式 - 泛型
        /// </summary>
        /// <typeparam name="IApplicationService"></typeparam>
        /// <param name="method"></param>
        /// <returns></returns>
        public IOContainer SetCreationMethod<IApplicationService>(CreationMethodEnum method)
        {
            return SetCreationMethod(typeof(IApplicationService), method);
        }

        /// <summary>
        ///  设置类型创建方式 - 类型
        /// </summary>
        /// <param name="iApplicationServiceType"></param>
        /// <param name="method"></param>
        /// <returns></returns>
        public IOContainer SetCreationMethod(Type iApplicationServiceType, CreationMethodEnum method)
        {
            var classInfo = InterfaceClassBinding[iApplicationServiceType] ?? throw new Exception(string.Format("Type {0} not registered.", iApplicationServiceType.FullName));
            classInfo.CreationMethod = method;

            return this;
        }

        /// <summary>
        /// 设置类型创建方式 - 程序集字符串
        /// </summary>
        /// <param name="iApplicationServiceStr"></param>
        /// <param name="method"></param>
        /// <returns></returns>
        public IOContainer SetCreationMethod(string iApplicationServiceStr, CreationMethodEnum method)
        {
            var iApplicationServiceType = GetTypeByString(iApplicationServiceStr);

            return SetCreationMethod(iApplicationServiceType, method);
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <returns>获取所绑定接口的类实例</returns>
        public object Get(Type iApplicationServiceType)
        {
            return Get(iApplicationServiceType, null, null);
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <typeparam name="IApplicationService">接口</typeparam>
        /// <returns>获取所绑定接口的类实例</returns>
        public IApplicationService Get<IApplicationService>()
        {
            return (IApplicationService)Get(typeof(IApplicationService), null, null);
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <typeparam name="IApplicationService">接口</typeparam>
        /// <param name="paramTypes">构造函数参数类型列表</param>
        /// <returns>获取所绑定接口的类实例</returns>
        public IApplicationService Get<IApplicationService>(Type[] paramTypes)
        {
            return (IApplicationService)Get(typeof(IApplicationService), paramTypes, null);
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <typeparam name="IApplicationService">接口</typeparam>
        /// <param name="paramTypes">构造函数参数类型列表</param>
        /// <param name="paramArray">构造函数参数列表</param>
        /// <returns>获取所绑定接口的类实例</returns>
        public IApplicationService Get<IApplicationService>(Type[] paramTypes, object[] paramArray)
        {
            return (IApplicationService)Get(typeof(IApplicationService), paramTypes, paramArray);
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <param name="IApplicationService">接口类型</param>
        /// <param name="paramTypes">构造函数参数类型列表</param>
        /// <param name="paramArray">构造函数参数列表</param>
        /// <returns>获取所绑定接口的类实例</returns>
        public object Get(Type IApplicationService, Type[] paramTypes, object[] paramArray)
        {
            // 判断接口类型是否注册
            if (!InterfaceClassBinding.ContainsKey(IApplicationService))
            {
                throw new Exception(string.Format("Type {0} not registered.", IApplicationService.FullName));
            }
            // 从字典获取类型
            var classInfo = InterfaceClassBinding[IApplicationService];
            // 获取接口绑定的类型
            Type ApplicationService = classInfo.ClassType;

            // 如果该类型是单例类并且已经被初始化，那么直接返回Instance实例
            if (classInfo.CreationMethod == CreationMethodEnum.Singleton && classInfo.Instance != null)
            {
                return classInfo.Instance;
            }
            else
            {
                // 初始化构造函数参数列表
                InitConstructorParams(classInfo, ref paramTypes, ref paramArray);
                // 获取符合类型的构造函数
                ConstructorInfo ctorInfo = ApplicationService.GetConstructor(paramTypes) ?? throw new Exception(string.Format("Class {0} not Types constructor!", IApplicationService.FullName));
                // 委托执行,新建该对象
                object retObject = ctorInfo.Invoke(paramArray);
                // 将创建好的对象备份给Instance
                classInfo.Instance = retObject;
                // 返回创建好的对象
                return retObject;
            }
        }

        /// <summary>
        /// 通过程序集类型名称获取Type
        /// </summary>
        /// <param name="assemblyTypeName"></param>
        /// <returns></returns>
        public Type GetTypeByString(string assemblyTypeName)
        {
            // 分割字符串
            var iservices = assemblyTypeName.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            // 判断参数是否正确
            if (iservices.Length < 2)
            {
                throw new ArgumentException("不能提供有效的AssemblyTypeName,AssemblyTypeName应当为\"程序集名称,程序名称\"");
            }

            // 加载程序集
            var iserviceAssembly = Assembly.Load(iservices[0]);
            // 查找类型
            return iserviceAssembly.GetTypes().Where(x => x.FullName == iservices[1]).FirstOrDefault() ?? throw new Exception($"没有找到{iservices[1]}类型！");
        }

        /// <summary>
        /// 初始化参数类型列表和参数列表
        /// </summary>
        /// <param name="paramTypes">参数类型列表</param>
        /// <param name="paramArray">参数列表</param>
        private void InitConstructorParams(ClassConfigInfo classInfo, ref Type[] paramTypes, ref object[] paramArray)
        {
            // 检查参数是否为空
            paramTypes = paramTypes ?? new Type[] { };
            paramArray = paramArray ?? new object[] { };

            // 如果参数类型表和参数数据为空
            // 
            if (paramTypes.Length == 0 && paramArray.Length == 0)
            {
                // 获取无参的构造函数
                var noParamConstrcut = classInfo.ClassType.GetConstructor(paramTypes);

                // 如果没有无参构造函数，那么使用获取的第一个构造函数
                if (noParamConstrcut == null)
                {
                    // 获取第一个构造函数
                    var constructorInfo = classInfo.ClassType.GetConstructors().First();
                    // 获取构造函数的参数类型列表
                    paramTypes = constructorInfo.GetParameters().Select(p => p.ParameterType).ToArray();
                }
            }

            // 如果类型列表不为0并参数列表为0的话,
            // 在容器内进行查找是否有相同的类型对象
            if (paramTypes.Length != 0 && paramArray.Length == 0)
            {
                List<object> paramList = new List<object>();
                foreach (Type type in paramTypes)
                {
                    paramList.Add(Get(type, null, null));
                }
                // 主要是用来做构造函数注入
                // 将容器内部查找到的对象放入paramArray
                paramArray = paramList.ToArray();
            }
        }
    }
}
